Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Oct 17, 2025

Summary by CodeRabbit

  • New Features

    • Policy support moved to an optional policy plugin providing @@allow/@@deny and runtime helpers; sample app demonstrates policy-enabled reads.
    • Schema loader now discovers and loads plugin model files automatically.
  • Bug Fixes

    • Plugin loading is tolerant of missing or non-exporting plugins (skips gracefully).
  • Chores

    • Packages bumped to 3.0.0-beta.10.

ymc9 and others added 2 commits October 16, 2025 21:52
* refactor: move policy attributes to its own zmodel

* update

* update

* fix tests
Copilot AI review requested due to automatic review settings October 17, 2025 04:53
@claude
Copy link

claude bot commented Oct 17, 2025

Claude finished @ymc9's task —— View job


PR Review Complete

After thoroughly reviewing this v3.0.0-beta.10 release merge, I've identified several key areas of feedback:

Review Checklist

  • Analyze overall changes and scope
  • Review policy attribute refactoring
  • Check version consistency across packages
  • Examine new policy plugin structure
  • Review utility file moves/renames
  • Assess security implications
  • Check for potential bugs
  • Evaluate performance considerations
  • Assess test coverage

🎯 Summary

This PR represents a significant architectural improvement by extracting policy attributes to a dedicated plugin.zmodel file. The refactoring enhances modularity while maintaining backward compatibility. Version consistency is good across packages.

Positive Changes

  1. Clean separation of concerns - Moving policy attributes from stdlib.zmodel to dedicated packages/plugins/policy/plugin.zmodel improves modularity
  2. Consistent version bumping - All packages properly updated to 3.0.0-beta.10
  3. Enhanced plugin architecture - New plugin discovery mechanism in action-utils.ts:55-109 is well-implemented
  4. Backward compatibility - Changes appear to maintain existing API contracts

⚠️ Areas of Concern

Code Quality Issues

  1. packages/cli/src/actions/action-utils.ts:98 - Potential import resolution vulnerability:

    const resolvedUrl = import.meta.resolve(`${provider}/${PLUGIN_MODULE_NAME}`);

    This could be exploited if provider contains malicious paths. Consider sanitizing the provider path.

  2. packages/language/src/validators/function-invocation-validator.ts:229 - TODO comment indicates incomplete migration:

    // TODO: move this to policy plugin
    @func('check')

    The check function validation should be moved to the policy plugin for consistency.

Performance Considerations

  1. packages/cli/src/actions/action-utils.ts:55-109 - Plugin discovery performs multiple file system operations:
    • Consider caching results or implementing async batching for better performance
    • The current implementation may be slow with many plugins

Security Implications

  1. Dynamic import resolution - The new plugin loading mechanism needs validation:
    • Ensure provider paths are properly sanitized
    • Consider implementing a whitelist of allowed plugin sources

🐛 Potential Bugs

  1. Missing error handling in tests/e2e/orm/scripts/generate.ts:26:

    const pluginDocs = [path.resolve(_dirname, '../../node_modules/@zenstackhq/plugin-policy/plugin.zmodel')];

    No check if the plugin file exists before attempting to load.

  2. File rename impact - Utility files were moved but need to verify all imports are updated:

    • kysely-utils.ts and schema-utils.ts moved within runtime package
    • Check for any missed import references

🧪 Test Coverage

  • Policy plugin tests appear adequate
  • Consider adding integration tests for the new plugin discovery mechanism
  • E2E test updates properly reflect the new plugin structure

📝 Recommendations

  1. Immediate: Sanitize provider paths in plugin discovery to prevent path traversal attacks
  2. Short-term: Complete the TODO migration of check function validation to policy plugin
  3. Future: Implement plugin discovery caching for performance improvement
  4. Documentation: Update migration guide for users affected by the policy attribute extraction

🔒 Security Assessment

  • Medium risk: Plugin discovery mechanism needs input validation
  • No other significant security vulnerabilities identified
  • Policy handling maintains appropriate access controls

This refactoring represents solid architectural progress. The main concerns are around input validation in plugin discovery and completing the migration of policy-specific validations.

@coderabbitai
Copy link

coderabbitai bot commented Oct 17, 2025

Warning

Rate limit exceeded

@ymc9 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 35 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6f48c74 and 05bf237.

📒 Files selected for processing (8)
  • packages/ide/vscode/package.json (1 hunks)
  • packages/ide/vscode/src/language-server/main.ts (1 hunks)
  • packages/language/src/document.ts (1 hunks)
  • packages/language/src/index.ts (1 hunks)
  • packages/language/src/module.ts (4 hunks)
  • packages/language/src/utils.ts (5 hunks)
  • packages/language/src/zmodel-workspace-manager.ts (1 hunks)
  • samples/blog/zenstack/schema.zmodel (3 hunks)

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Move policy DSL from stdlib into a new policy plugin, add plugin discovery and plugin-aware document loading across CLI and test tooling, update runtime/sdk exports and policy handlers to use runtime-provided utils, adjust validators to validate policy functions universally, and bump workspace packages to 3.0.0-beta.10.

Changes

Cohort / File(s) Summary
Version bumps
\package.json`, `packages//package.json`, `tests//package.json`, `samples/blog/package.json``
Bump root and workspace packages from 3.0.0-beta.93.0.0-beta.10.
Policy plugin artifact & packaging
\packages/plugins/policy/plugin.zmodel`, `packages/plugins/policy/package.json``
Add plugin.zmodel exposing @@allow/@@deny, check, before, currentModel, currentOperation; publish plugin.zmodel via package exports; remove @zenstackhq/sdk dep.
Stdlib policy removal
\packages/language/res/stdlib.zmodel``
Remove policy attributes and runtime helpers from stdlib (policy DSL moved to plugin).
Plugin discovery & CLI changes
\packages/cli/src/actions/action-utils.ts`, `packages/cli/src/actions/generate.ts`, `packages/cli/src/constants.ts``
Add getPluginDocuments() to locate plugin.zmodel files, introduce PLUGIN_MODULE_NAME = 'plugin.zmodel', and make CLI plugin imports tolerant of missing/non-exporting modules.
Language surface & validators
\packages/language/src/index.ts`, `packages/language/src/utils.ts`, `packages/language/src/validators/function-invocation-validator.ts`, `packages/language/test/utils.ts``
Rename pluginDocsadditionalDocs; relax isBeforeInvocation() stdlib-origin check; remove stdlib-only guard so function-invocation validation runs universally; tests updated to provide plugin docs.
Runtime & SDK exports / utils
\packages/runtime/src/index.ts`, `packages/runtime/src/utils/schema-utils.ts`, `packages/sdk/src/index.ts`, `packages/sdk/src/model-utils.ts``
Export new namespaces KyselyUtils and SchemaUtils; adjust internal imports/exports (remove some re-exports, add ModelUtils), and fix import paths.
Policy implementation updates
\packages/plugins/policy/src/column-collector.ts`, `packages/plugins/policy/src/policy-handler.ts``
Change visitor base classes/imports to use runtime-provided KyselyUtils/SchemaUtils types (adjust imports/inheritance).
Test tooling: plugin-aware loaders
\packages/testtools/src/utils.ts`, `packages/testtools/src/schema.ts`, `packages/testtools/src/client.ts``
Add loadDocumentWithPlugins() helper and switch internal callers to load documents with discovered plugin model files.
Samples & schemas
\samples/blog/zenstack/schema.ts`, `samples/blog/zenstack/schema.zmodel`, `samples/blog/main.ts`, `samples/blog/zenstack/models.ts`, `.vscode/launch.json``
Add plugin policy reference and @@allow rules to models; sample main.ts registers PolicyPlugin; minor doc/launch edits.
Tests & generator scripts
\tests/e2e/orm/policy/migrated/current-*.test.ts`, `tests/e2e/orm/scripts/generate.ts`, `tests/e2e/package.json``
Add @id to test model id fields; generator script passes plugin docs and casts model type; bump e2e package version.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐇 I nudged the stdlib, found a plug-in den,

rules took root where modules blend.
Sniffed for petals named "plugin.zmodel",
hopped them home to keep the schema nimble.
A tiny hop for tidy code — nibble, nibble! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "merge dev to main (v3.0.0-beta.10)" is partially related to the changeset. It accurately conveys that this is a merge operation between branches and identifies the version number, which is relevant since multiple package.json files are bumped from 3.0.0-beta.9 to 3.0.0-beta.10. However, the title is overly broad and does not capture the main technical changes in the changeset, such as the significant refactoring of policy functionality from stdlib to a separate plugin system, the introduction of plugin document discovery, or the various API changes in the runtime and SDK. A developer scanning commit history would understand this is a version release and merge operation, but not the specific engineering work performed.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR merges the dev branch to main for version 3.0.0-beta.10. The key change is the extraction of policy plugin functionality from the core language package into a standalone plugin package, with supporting infrastructure updates to enable dynamic plugin model loading.

Key changes:

  • Policy-related definitions moved from stdlib.zmodel to a new plugin.zmodel file in the policy plugin package
  • Updated module loading infrastructure to support dynamic plugin model files
  • Version bumps across all packages to 3.0.0-beta.10

Reviewed Changes

Copilot reviewed 44 out of 46 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/plugins/policy/plugin.zmodel New file defining policy-specific attributes and functions previously in stdlib
packages/plugins/policy/package.json Updated to export plugin.zmodel and remove @zenstackhq/sdk dependency
packages/language/res/stdlib.zmodel Removed policy-related definitions now in plugin.zmodel
packages/cli/src/actions/action-utils.ts Added getPluginDocuments function to dynamically discover and load plugin models
packages/testtools/src/utils.ts New utility function to load documents with plugin models
packages/runtime/src/index.ts Exported KyselyUtils and SchemaUtils for use by plugins
packages/sdk/src/index.ts Removed exports no longer needed externally
samples/blog/zenstack/schema.zmodel Added policy plugin declaration and access policies
Multiple package.json files Version bumps to 3.0.0-beta.10
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
samples/blog/main.ts (1)

94-103: Consider adding error handling for policy violations.

The policy-enabled read demonstration shows the correct usage pattern:

  1. Apply PolicyPlugin via $use
  2. Set authentication context with $setAuth
  3. Execute queries that respect policy rules

However, the code lacks error handling for potential policy violations. When queries violate access policies, they may throw RejectedByPolicyError exceptions.

Consider wrapping the policy-enabled queries in try-catch blocks to demonstrate error handling:

     // policy-enabled read
     const authDb = db.$use(new PolicyPlugin());
     const user1Db = authDb.$setAuth({ id: user1.id });
     const user2Db = authDb.$setAuth({ id: user2.id });
 
-    console.log('Posts readable to', user1.email);
-    console.table(await user1Db.post.findMany({ select: { title: true, published: true } }));
-
-    console.log('Posts readable to', user2.email);
-    console.table(await user2Db.post.findMany({ select: { title: true, published: true } }));
+    try {
+        console.log('Posts readable to', user1.email);
+        console.table(await user1Db.post.findMany({ select: { title: true, published: true } }));
+    } catch (error) {
+        console.error('Policy violation for user1:', error);
+    }
+
+    try {
+        console.log('Posts readable to', user2.email);
+        console.table(await user2Db.post.findMany({ select: { title: true, published: true } }));
+    } catch (error) {
+        console.error('Policy violation for user2:', error);
+    }
tests/e2e/orm/scripts/generate.ts (1)

22-33: Resolve plugin.zmodel via ESM instead of hard-coded path

Hard-coding ../../node_modules is brittle across workspaces. Use import.meta.resolve to locate the plugin’s plugin.zmodel.

Apply:

-    // isomorphic __dirname
-    const _dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
-
-    // plugin models
-    const pluginDocs = [path.resolve(_dirname, '../../node_modules/@zenstackhq/plugin-policy/plugin.zmodel')];
-
-    const result = await loadDocument(schemaPath, pluginDocs);
+    // plugin models (resolve via ESM)
+    let pluginDocs: string[] = [];
+    try {
+        const url = import.meta.resolve('@zenstackhq/plugin-policy/plugin.zmodel');
+        pluginDocs = [fileURLToPath(url)];
+    } catch {
+        // fallback: proceed without plugin docs if not available
+    }
+    const result = await loadDocument(schemaPath, pluginDocs);
packages/cli/src/actions/generate.ts (1)

67-89: Don’t fully silence plugin import failures; warn when skipping

Swallowing all errors makes diagnosing plugin issues hard. Emit a warning (honoring options.silent) when a provider can’t be loaded as a generator.

Apply:

-            try {
-                cliPlugin = (await import(moduleSpec)).default as CliPlugin;
-            } catch {
-                // plugin may not export a generator so we simply ignore the error here
-            }
+            try {
+                cliPlugin = (await import(moduleSpec)).default as CliPlugin;
+            } catch (e) {
+                if (!options.silent) {
+                    console.warn(
+                        colors.yellow(
+                            `Plugin "${provider}" couldn't be loaded as a CLI generator, skipping${
+                                e instanceof Error && e.message ? `: ${e.message}` : ''
+                            }`,
+                        ),
+                    );
+                }
+            }
packages/language/src/validators/function-invocation-validator.ts (1)

54-87: Universal context validation + casing checks look solid

Traversal to enclosing attribute and expression-context enforcement aligns with plugin functions. Nice.

If desired, consider extending getExpressionContext to treat '@id', '@@id', '@unique', '@@unique' as Index as well.

packages/cli/src/actions/action-utils.ts (1)

55-111: Plugin doc discovery is good; add small hardening

  • Dedupe discovered plugin files to avoid double-builds.
  • Optionally add a require.resolve fallback when import.meta.resolve is unavailable.

Minimal dedupe:

-    return result;
+    return [...new Set(result)];

Optional fallback (if you choose to support non-ESM resolution environments), replace the import.meta.resolve try-block with:

-        if (!pluginModelFile) {
-            // try loading it as a ESM module
-            try {
-                const resolvedUrl = import.meta.resolve(`${provider}/${PLUGIN_MODULE_NAME}`);
-                pluginModelFile = fileURLToPath(resolvedUrl);
-            } catch {
-                // noop
-            }
-        }
+        if (!pluginModelFile) {
+            // try loading it as an ESM module, fall back to require.resolve
+            try {
+                const resolvedUrl = import.meta.resolve(`${provider}/${PLUGIN_MODULE_NAME}`);
+                pluginModelFile = fileURLToPath(resolvedUrl);
+            } catch {
+                try {
+                    const { createRequire } = await import('node:module');
+                    const req = createRequire(import.meta.url);
+                    pluginModelFile = req.resolve(`${provider}/${PLUGIN_MODULE_NAME}`);
+                } catch {
+                    // noop
+                }
+            }
+        }

Also confirm the minimum Node version for the CLI supports import.meta.resolve.

packages/plugins/policy/plugin.zmodel (2)

7-7: Improve formatting consistency in completionHint array.

The completionHint array has inconsistent spacing after 'post-update'.

Apply this diff for consistent spacing:

-attribute @@allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'post-update'","'delete'", "'all'"]), _ condition: Boolean)
+attribute @@allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'post-update'", "'delete'", "'all'"]), _ condition: Boolean)

25-25: Improve formatting consistency in completionHint array.

The completionHint array has inconsistent spacing after 'post-update'.

Apply this diff for consistent spacing:

-attribute @@deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'post-update'","'delete'", "'all'"]), _ condition: Boolean)
+attribute @@deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'post-update'", "'delete'", "'all'"]), _ condition: Boolean)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68adf79 and 7ef504c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (44)
  • .vscode/launch.json (1 hunks)
  • package.json (1 hunks)
  • packages/cli/package.json (1 hunks)
  • packages/cli/src/actions/action-utils.ts (3 hunks)
  • packages/cli/src/actions/generate.ts (2 hunks)
  • packages/cli/src/constants.ts (1 hunks)
  • packages/common-helpers/package.json (1 hunks)
  • packages/config/eslint-config/package.json (1 hunks)
  • packages/config/typescript-config/package.json (1 hunks)
  • packages/config/vitest-config/package.json (1 hunks)
  • packages/create-zenstack/package.json (1 hunks)
  • packages/dialects/sql.js/package.json (1 hunks)
  • packages/language/package.json (1 hunks)
  • packages/language/res/stdlib.zmodel (0 hunks)
  • packages/language/src/index.ts (2 hunks)
  • packages/language/src/utils.ts (1 hunks)
  • packages/language/src/validators/function-invocation-validator.ts (1 hunks)
  • packages/language/test/utils.ts (2 hunks)
  • packages/plugins/policy/package.json (3 hunks)
  • packages/plugins/policy/plugin.zmodel (1 hunks)
  • packages/plugins/policy/src/column-collector.ts (1 hunks)
  • packages/plugins/policy/src/policy-handler.ts (2 hunks)
  • packages/runtime/package.json (1 hunks)
  • packages/runtime/src/index.ts (1 hunks)
  • packages/runtime/src/utils/schema-utils.ts (1 hunks)
  • packages/sdk/package.json (1 hunks)
  • packages/sdk/src/index.ts (0 hunks)
  • packages/sdk/src/model-utils.ts (1 hunks)
  • packages/tanstack-query/package.json (1 hunks)
  • packages/testtools/package.json (1 hunks)
  • packages/testtools/src/client.ts (2 hunks)
  • packages/testtools/src/schema.ts (5 hunks)
  • packages/testtools/src/utils.ts (1 hunks)
  • packages/zod/package.json (1 hunks)
  • samples/blog/main.ts (2 hunks)
  • samples/blog/package.json (2 hunks)
  • samples/blog/zenstack/models.ts (0 hunks)
  • samples/blog/zenstack/schema.ts (3 hunks)
  • samples/blog/zenstack/schema.zmodel (3 hunks)
  • tests/e2e/orm/policy/migrated/current-model.test.ts (1 hunks)
  • tests/e2e/orm/policy/migrated/current-operation.test.ts (1 hunks)
  • tests/e2e/orm/scripts/generate.ts (2 hunks)
  • tests/e2e/package.json (1 hunks)
  • tests/regression/package.json (1 hunks)
💤 Files with no reviewable changes (3)
  • samples/blog/zenstack/models.ts
  • packages/language/res/stdlib.zmodel
  • packages/sdk/src/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/config/eslint-config/package.json
  • packages/cli/package.json
  • packages/sdk/package.json
  • tests/regression/package.json
  • packages/runtime/src/index.ts
  • packages/cli/src/constants.ts
  • packages/plugins/policy/package.json
  • packages/language/src/index.ts
  • packages/testtools/src/schema.ts
  • packages/testtools/src/client.ts
  • packages/testtools/src/utils.ts
  • packages/language/test/utils.ts
  • packages/cli/src/actions/generate.ts
  • tests/e2e/orm/policy/migrated/current-model.test.ts
  • packages/testtools/package.json
  • packages/plugins/policy/src/policy-handler.ts
  • packages/common-helpers/package.json
  • packages/tanstack-query/package.json
  • packages/runtime/package.json
  • packages/zod/package.json
  • packages/config/typescript-config/package.json
  • packages/plugins/policy/src/column-collector.ts
  • packages/language/src/validators/function-invocation-validator.ts
  • samples/blog/zenstack/schema.ts
  • packages/sdk/src/model-utils.ts
  • samples/blog/package.json
  • tests/e2e/orm/policy/migrated/current-operation.test.ts
  • tests/e2e/package.json
  • tests/e2e/orm/scripts/generate.ts
  • packages/config/vitest-config/package.json
  • packages/runtime/src/utils/schema-utils.ts
  • packages/create-zenstack/package.json
  • packages/cli/src/actions/action-utils.ts
  • packages/plugins/policy/plugin.zmodel
  • packages/language/package.json
  • packages/language/src/utils.ts
  • samples/blog/main.ts
  • packages/dialects/sql.js/package.json
  • samples/blog/zenstack/schema.zmodel
**/schema.ts

📄 CodeRabbit inference engine (CLAUDE.md)

The generated TypeScript schema should be named schema.ts

Files:

  • packages/testtools/src/schema.ts
  • samples/blog/zenstack/schema.ts
tests/e2e/**

📄 CodeRabbit inference engine (CLAUDE.md)

End-to-end tests must live under tests/e2e/

Files:

  • tests/e2e/orm/policy/migrated/current-model.test.ts
  • tests/e2e/orm/policy/migrated/current-operation.test.ts
  • tests/e2e/package.json
  • tests/e2e/orm/scripts/generate.ts
package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Pin the repository package manager to [email protected] via the packageManager field

Files:

  • package.json
**/schema.zmodel

📄 CodeRabbit inference engine (CLAUDE.md)

Name ZModel schema files schema.zmodel

Files:

  • samples/blog/zenstack/schema.zmodel
🧠 Learnings (1)
📚 Learning: 2025-09-04T12:38:14.150Z
Learnt from: CR
PR: zenstackhq/zenstack-v3#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-04T12:38:14.150Z
Learning: Applies to **/schema.ts : The generated TypeScript schema should be named `schema.ts`

Applied to files:

  • tests/e2e/orm/scripts/generate.ts
🧬 Code graph analysis (11)
packages/testtools/src/schema.ts (1)
packages/testtools/src/utils.ts (1)
  • loadDocumentWithPlugins (4-7)
packages/testtools/src/client.ts (1)
packages/testtools/src/utils.ts (1)
  • loadDocumentWithPlugins (4-7)
packages/testtools/src/utils.ts (1)
packages/language/src/index.ts (1)
  • loadDocument (21-136)
packages/language/test/utils.ts (2)
packages/testtools/src/schema.ts (1)
  • loadSchema (94-124)
packages/language/src/index.ts (1)
  • loadDocument (21-136)
packages/cli/src/actions/generate.ts (1)
packages/sdk/src/cli-plugin.ts (1)
  • CliPlugin (32-47)
packages/language/src/validators/function-invocation-validator.ts (2)
packages/language/src/generated/ast.ts (6)
  • DataModelAttribute (395-400)
  • DataModelAttribute (402-402)
  • DataFieldAttribute (348-353)
  • DataFieldAttribute (355-355)
  • isDataModelAttribute (404-406)
  • isDataFieldAttribute (357-359)
packages/language/src/utils.ts (2)
  • getFunctionExpressionContext (333-347)
  • getLiteral (268-282)
samples/blog/zenstack/schema.ts (1)
packages/runtime/src/schema/expression.ts (1)
  • ExpressionUtils (16-120)
tests/e2e/orm/scripts/generate.ts (1)
packages/language/src/index.ts (1)
  • loadDocument (21-136)
packages/cli/src/actions/action-utils.ts (5)
packages/language/src/index.ts (2)
  • createZModelServices (11-13)
  • loadDocument (21-136)
packages/language/src/module.ts (1)
  • ZModelServices (31-31)
packages/language/src/generated/ast.ts (1)
  • isPlugin (628-630)
packages/language/src/utils.ts (1)
  • getLiteral (268-282)
packages/cli/src/constants.ts (1)
  • PLUGIN_MODULE_NAME (5-5)
packages/language/src/utils.ts (2)
packages/language/src/ast.ts (2)
  • AstNode (4-4)
  • AstNode (66-71)
packages/language/src/generated/ast.ts (1)
  • isInvocationExpr (542-544)
samples/blog/main.ts (1)
packages/plugins/policy/src/plugin.ts (1)
  • PolicyPlugin (6-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: build-test (20.x, postgresql)
🔇 Additional comments (48)
packages/language/package.json (1)

4-4: Version bump consistent with release cycle. No concerns.

packages/cli/package.json (1)

6-6: Version bump consistent with release cycle. No concerns.

packages/create-zenstack/package.json (1)

3-3: Version bump consistent with release cycle. No concerns.

packages/config/typescript-config/package.json (1)

3-3: Version bump consistent with release cycle. No concerns.

samples/blog/package.json (1)

3-3: Version bump consistent; new policy plugin dependency appropriately added to sample dependencies.

The addition of @zenstackhq/plugin-policy enables the blog sample to demonstrate policy-driven access patterns, aligning with the new plugin-based architecture introduced in this PR.

Also applies to: 18-18

package.json (1)

3-3: Root package version and packageManager correctly configured.

The packageManager field is correctly pinned to [email protected] per coding guidelines. Version bump is consistent with all subordinate packages.

Also applies to: 5-5

tests/regression/package.json (1)

3-3: Version bump consistent with release cycle. No concerns.

tests/e2e/package.json (1)

3-3: Version bump and policy plugin dependency verified as correct.

The version 3.0.0-beta.10 is consistent with the workspace packages, and @zenstackhq/plugin-policy is a valid workspace package at packages/plugins/policy/ with correct workspace:* reference. The dependency addition enables policy-driven tests aligned with the plugin-based architecture refactor.

packages/testtools/package.json (1)

3-3: LGTM! Version bump aligned with release.

The version bump to 3.0.0-beta.10 is consistent with the coordinated release across all packages in this PR.

packages/language/src/utils.ts (1)

450-453: Confirm the semantic change aligns with plugin refactoring.

The TODO comment and function implementation suggest that before invocations are no longer required to originate from the stdlib, which aligns with moving policy declarations to a plugin-based architecture. This is a semantic change that broadens the scope of what constitutes a "before invocation."

Ensure that:

  1. Downstream code that relies on isBeforeInvocation can handle invocations from plugin sources.
  2. This change is consistent with how isCheckInvocation (line 360-362) is already implemented.

Based on learnings about ZenStack's V3 plugin architecture refactoring.

packages/zod/package.json (1)

3-3: LGTM! Version bump aligned with release.

The version bump to 3.0.0-beta.10 is part of the coordinated release across all packages.

packages/config/eslint-config/package.json (1)

3-3: LGTM! Version bump aligned with release.

The version bump to 3.0.0-beta.10 is consistent with the coordinated release.

packages/common-helpers/package.json (1)

3-3: LGTM! Version bump aligned with release.

The version bump to 3.0.0-beta.10 is part of the coordinated release across all packages.

packages/runtime/package.json (1)

3-3: LGTM! Version bump aligned with release.

The version bump to 3.0.0-beta.10 is part of the coordinated release across all packages.

packages/sdk/src/model-utils.ts (1)

16-16: LGTM! Type-only import is a best practice.

Changing Model to a type-only import is appropriate since it's only used in type annotations (line 90, 104). This improves bundle size and makes the import intent explicit.

.vscode/launch.json (1)

14-14: Based on the verification of the CLI implementation, the working directory change is correct and necessary.

The ZenStack CLI defaults to searching for zenstack/schema.zmodel relative to the current working directory. By changing the working directory from ${workspaceFolder}/samples/blog/zenstack to ${workspaceFolder}/samples/blog, the generate command will now correctly locate the schema file at the expected zenstack/schema.zmodel path.

The schema file is confirmed at samples/blog/zenstack/schema.zmodel, and this change aligns with the CLI's default schema discovery behavior.

packages/tanstack-query/package.json (1)

3-3: LGTM! Version bump is consistent with the release.

The version increment from beta.9 to beta.10 aligns with the PR's objective to release v3.0.0-beta.10.

packages/dialects/sql.js/package.json (1)

3-3: LGTM! Version bump is consistent with the release.

The version increment from beta.9 to beta.10 aligns with the PR's objective to release v3.0.0-beta.10.

packages/config/vitest-config/package.json (1)

4-4: LGTM! Version bump is consistent with the release.

The version increment from beta.9 to beta.10 aligns with the PR's objective to release v3.0.0-beta.10.

packages/cli/src/constants.ts (1)

4-5: LGTM! Well-documented constant for plugin discovery.

The constant is clearly named and documented, providing a standardized filename for plugin-contributed model files that will be used during plugin discovery.

packages/language/src/index.ts (1)

54-72: LGTM! Improved variable naming.

The renaming from pluginDocs to additionalDocs better reflects the general-purpose nature of the additionalModelFiles parameter. The usage is consistent throughout the function.

packages/language/test/utils.ts (3)

8-8: LGTM! Hardcoded path is appropriate for test utilities.

The hardcoded path to the policy plugin using __dirname for relative resolution is acceptable in test utilities and will correctly resolve to the plugin source in the monorepo structure.


14-14: LGTM! Plugin-aware document loading.

The addition of pluginDocs as the second argument enables plugin-aware schema loading in the test utility, aligning with the PR's objective to support plugin-based policy architecture.


28-28: LGTM! Consistent plugin loading in error case.

Plugin documents are correctly passed to loadDocument in the error test scenario as well, ensuring consistent behavior across test helpers.

packages/testtools/src/utils.ts (1)

4-7: LGTM! Plugin-aware document loader for test tooling.

The helper correctly resolves the plugin path from node_modules using __dirname, which is appropriate for test tooling that depends on installed packages. The workspace dependency structure should ensure the plugin package is available.

packages/testtools/src/client.ts (2)

17-17: LGTM! Updated to use plugin-aware document loading.

The import change from loadDocument to loadDocumentWithPlugins correctly enables plugin support in the test client infrastructure, aligning with the PR's plugin-based architecture refactoring.


119-119: LGTM! Consistent plugin loading in Prisma push flow.

The call to loadDocumentWithPlugins ensures that plugin model files are loaded when using Prisma DB push, maintaining consistency with the new plugin-aware loading strategy.

packages/plugins/policy/package.json (2)

3-3: LGTM: Version bump aligns with PR release.

The version bump to 3.0.0-beta.10 is consistent with the PR's objective to release this beta version.


15-18: Verification confirmed: plugin.zmodel file exists and contains valid policy DSL declarations.

The file is properly structured with all expected policy-related attributes and helper functions (@@allow, @@deny, check(), before(), currentModel(), currentOperation()). The declarations are complete and correctly documented for the plugin-based policy architecture.

samples/blog/zenstack/schema.ts (4)

1-4: LGTM: Generated file with appropriate warning.

The file header correctly indicates this is auto-generated by ZenStack CLI and should not be manually modified.


72-75: Review: User model policy rules appear correct.

The User model includes two access control rules:

  1. Read and create operations allowed for all users (condition: true)
  2. All operations allowed when the authenticated user matches the resource (this == auth)

This is a common pattern for user self-management. However, verify that allowing "create" globally aligns with your authorization requirements—this permits any user (including unauthenticated) to create User records, which might be intentional for registration but should be confirmed.


139-141: Review: Profile model policy uses check function.

The Profile model allows all operations when check(user) returns true. Based on the retrieved learnings and the policy plugin structure, the check function evaluates the policy of the related entity.

Verify that this delegation pattern is intentional—it means Profile accessibility is entirely determined by the associated User's policies.


198-201: LGTM: Post model policy rules are appropriate.

The Post model has sensible access control:

  1. Read allowed for published posts
  2. All operations allowed when the author matches the authenticated user

This implements a standard content visibility pattern where authors can manage their own posts and published posts are publicly readable.

packages/sdk/package.json (1)

3-3: LGTM: Version bump aligns with PR release.

The version bump to 3.0.0-beta.10 is consistent with the PR's objective.

packages/runtime/src/utils/schema-utils.ts (1)

13-13: LGTM: Import path updated to match directory structure.

The import path change from './schema' to '../schema' adjusts to the module structure. This is a straightforward refactor with no runtime impact.

packages/plugins/policy/src/column-collector.ts (1)

1-1: LGTM: Updated to use runtime's KyselyUtils abstraction.

The change from directly importing Kysely internals to using KyselyUtils.DefaultOperationNodeVisitor from the runtime package improves the abstraction layer and aligns with the removal of @zenstackhq/sdk dependency noted in the PR summary.

Also applies to: 7-7

samples/blog/main.ts (1)

1-1: LGTM: PolicyPlugin import added.

The import statement correctly brings in the PolicyPlugin from the new plugin package.

packages/plugins/policy/src/policy-handler.ts (2)

10-10: LGTM: Added SchemaUtils import from runtime.

The import of SchemaUtils from @zenstackhq/runtime aligns with the architectural refactoring to remove the @zenstackhq/sdk dependency from the policy plugin.


273-281: LGTM: Updated to use runtime's SchemaUtils.ExpressionVisitor.

The anonymous class now extends SchemaUtils.ExpressionVisitor instead of directly importing from SDK. This change:

  1. Removes the SDK dependency from the policy plugin
  2. Uses the runtime's abstraction layer
  3. Maintains the same visitor pattern functionality

This aligns with the broader refactoring to make the policy plugin independent of SDK internals.

tests/e2e/orm/policy/migrated/current-model.test.ts (1)

161-161: Negative test update looks correct

Adding @id keeps the error context as DefaultValue; expectation remains valid. LGTM.

packages/runtime/src/index.ts (1)

2-3: Namespace re‑exports look good

Exposes utils under stable names. Verify type declarations include these namespaces in the published package.

tests/e2e/orm/policy/migrated/current-operation.test.ts (1)

130-130: LGTM for the negative test tweak

Adding @id keeps the invalid DefaultValue context; assertion remains correct.

packages/plugins/policy/plugin.zmodel (6)

9-17: Verify the purpose of the commented field-level @allow attribute.

The field-level @allow attribute declaration is commented out without explanation. Clarify whether this feature is:

  • Not yet implemented and should remain commented
  • Deprecated and should be removed
  • Ready to be enabled

27-33: Verify the purpose of the commented field-level @deny attribute.

Similar to @allow, the field-level @deny attribute is commented out. Additionally, note that the commented @allow documentation mentions a third override parameter, but this @deny declaration doesn't include it. Clarify:

  • Whether this feature should be enabled or removed
  • Whether an override parameter should be added for consistency with @allow

35-43: LGTM!

The check function is well-documented and the signature correctly matches its intended purpose for verifying access permissions on fields.


45-49: LGTM!

The before function is properly documented with a clear explanation of its usage context (post-update policies).


51-60: LGTM!

The currentModel function is well-documented with clear parameter options for casing transformation.


22-22: Fix typo in the documentation.

Same typo as in line 4: " "delete" should be "delete".

Apply this diff to fix the typo:

- * @param operation: comma-separated list of "create", "read", "update", "post-update", "delete". Use "all" to denote all operations.
+ * @param operation: comma-separated list of "create", "read", "update", "post-update", "delete". Use "all" to denote all operations.

Likely an incorrect or invalid review comment.

@claude
Copy link

claude bot commented Oct 17, 2025

Claude finished @ymc9's task —— View job


PR Review for v3.0.0-beta.10 Policy Plugin Refactoring

I'll provide a comprehensive review covering code quality, potential bugs, performance, security, and test coverage.

Review Checklist

  • Analyze overall architectural changes and scope
  • Review plugin discovery mechanism for security/reliability
  • Check path resolution approaches and potential issues
  • Examine version consistency across packages
  • Assess security implications of dynamic plugin loading
  • Review test coverage and integration points
  • Evaluate performance considerations
  • Check for potential bugs and edge cases

🎯 Summary

This PR represents a significant architectural improvement by extracting policy attributes (@@allow, @@deny) from the core stdlib.zmodel to a dedicated plugin.zmodel file in the policy plugin package. The refactoring enhances modularity and separation of concerns while maintaining backward compatibility.

Positive Changes

  1. Clean architectural separation - Moving policy declarations from packages/language/res/stdlib.zmodel to packages/plugins/policy/plugin.zmodel improves modularity
  2. Robust plugin discovery - The new getPluginDocuments() function provides multiple fallback mechanisms for locating plugin model files
  3. Consistent version management - All 14 packages correctly bumped to 3.0.0-beta.10
  4. Backward compatibility maintained - Existing APIs and user schemas continue to work

⚠️ Critical Issues

🔴 Security Vulnerability - Plugin Path Injection

Location: packages/cli/src/actions/action-utils.ts:98

const resolvedUrl = import.meta.resolve(`${provider}/${PLUGIN_MODULE_NAME}`);

Risk: The provider value from user schemas is directly interpolated into import.meta.resolve() without sanitization. Malicious schemas could inject arbitrary module paths:

plugin malicious {
    provider = "../../../etc/passwd"  // Path traversal attempt
}

Recommendation: Sanitize provider paths and implement a whitelist of allowed plugin sources.

🟡 Path Resolution Fragility

Location: packages/testtools/src/utils.ts:4 and tests/e2e/orm/scripts/generate.ts:26

Issues:

  1. Testtools uses require.resolve() - This works but may fail in pure ESM environments
  2. E2E tests use hardcoded relative paths - path.resolve(_dirname, '../../node_modules/@zenstackhq/plugin-policy/plugin.zmodel') is fragile across different execution contexts

Recommendation: Use import.meta.resolve() consistently with proper error handling.

🟠 Incomplete Migration

Location: packages/language/src/validators/function-invocation-validator.ts:229-232

// TODO: move this to policy plugin
@func('check')
private _checkCheck(expr: InvocationExpr, accept: ValidationAcceptor) {

Issue: The check function validation remains in the language package instead of being moved to the policy plugin, creating inconsistency.

🐛 Potential Bugs

  1. Missing error handling in generate.ts:81-83:

    try {
        cliPlugin = (await import(moduleSpec)).default as CliPlugin;
    } catch {
        // plugin may not export a generator so we simply ignore the error here
    }

    Silent failures make debugging plugin issues difficult.

  2. Documentation inconsistency in plugin.zmodel:64-65:

    "all" operation is expanded to "create", "read", "update", and "delete" rules
    

    This excludes "post-update" which is mentioned elsewhere in the file.

  3. Provider path inconsistency in samples/blog/zenstack/schema.zmodel:13:

    plugin policy {
        provider = '../node_modules/@zenstackhq/plugin-policy/dist/index.js'
    }

    Hardcoded relative paths are fragile. Should use package name: '@zenstackhq/plugin-policy'

🔒 Security Assessment

  • High Risk: Plugin discovery mechanism needs input validation to prevent path injection
  • Medium Risk: Dynamic import resolution could be exploited without proper sanitization
  • Low Risk: Policy handling maintains appropriate access controls

Performance Considerations

  1. Plugin discovery overhead - getPluginDocuments() performs multiple filesystem operations and could benefit from caching
  2. Multiple resolution attempts - The fallback chain (file path → ESM resolution) adds latency but provides robustness

🧪 Test Coverage

  • Good: Policy plugin tests appear comprehensive
  • Missing: No integration tests for the new plugin discovery mechanism
  • Fragile: E2E test path resolution may break in different environments

📝 Recommendations

Immediate (Security)

  1. Sanitize plugin provider paths in getPluginDocuments() to prevent path injection
  2. Implement plugin source whitelist for production environments

Short-term (Consistency)

  1. Complete the migration of check function validation to policy plugin
  2. Standardize path resolution using import.meta.resolve() across all contexts
  3. Add integration tests for plugin discovery mechanism

Medium-term (Performance)

  1. Implement caching for plugin discovery results
  2. Add plugin discovery performance metrics

🎯 Verdict

This refactoring represents solid architectural progress with good separation of concerns. The main security concern around plugin path injection should be addressed before merging. The other issues are mostly about consistency and robustness.

Overall Assessment: Approve with required security fixes


Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/plugins/policy/plugin.zmodel (1)

62-72: Update documentation to include "post-update" in the "all" operation expansion.

Line 64 states that "all" expands to "create", "read", "update", and "delete", but "post-update" is consistently referenced as a valid operation throughout this file (lines 4, 13, 22, 30, 39) and is included in the completion hints (lines 7, 25). The documentation should clarify whether "post-update" is included in the "all" expansion.

If "post-update" should be included in the expansion, apply this diff:

  * The operation for which the policy rule is defined for. Note that a rule with
- * "all" operation is expanded to "create", "read", "update", and "delete" rules,
+ * "all" operation is expanded to "create", "read", "update", "post-update", and "delete" rules,
  * and the function returns corresponding value for each expanded version.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ef504c and 6c8f756.

📒 Files selected for processing (2)
  • packages/plugins/policy/plugin.zmodel (1 hunks)
  • packages/testtools/src/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/testtools/src/utils.ts
🧰 Additional context used
📓 Path-based instructions (1)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/plugins/policy/plugin.zmodel
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-test (20.x, postgresql)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: claude-review

@claude
Copy link

claude bot commented Oct 17, 2025

Claude finished @ymc9's task —— View job


PR Review for v3.0.0-beta.10 Policy Plugin Refactoring

I'll provide a comprehensive review covering code quality, potential bugs, performance, security, and test coverage.

Review Checklist

  • Analyze overall architectural changes and scope
  • Review plugin discovery mechanism for security/reliability
  • Check path resolution approaches and potential issues
  • Examine version consistency across packages
  • Assess security implications of dynamic plugin loading
  • Review test coverage and integration points
  • Evaluate performance considerations
  • Check for potential bugs and edge cases

🎯 Summary

This PR represents a significant architectural improvement by extracting policy attributes (@@allow, @@deny) from the core stdlib.zmodel to a dedicated plugin.zmodel file in the policy plugin package. The refactoring enhances modularity and separation of concerns while maintaining backward compatibility.

Positive Changes

  1. Clean architectural separation - Moving policy declarations from packages/language/res/stdlib.zmodel to packages/plugins/policy/plugin.zmodel improves modularity
  2. Robust plugin discovery - The new getPluginDocuments() function provides multiple fallback mechanisms for locating plugin model files
  3. Consistent version management - All 18 packages correctly bumped to 3.0.0-beta.10
  4. Backward compatibility maintained - Existing APIs and user schemas continue to work

⚠️ Critical Issues

🔴 Security Vulnerability - Plugin Path Injection

Location: packages/cli/src/actions/action-utils.ts:98

const resolvedUrl = import.meta.resolve(`${provider}/${PLUGIN_MODULE_NAME}`);

Risk: The provider value from user schemas is directly interpolated into import.meta.resolve() without sanitization. Malicious schemas could inject arbitrary module paths:

plugin malicious {
    provider = "../../../etc/passwd"  // Path traversal attempt
}

Recommendation: Sanitize provider paths and implement a whitelist of allowed plugin sources.

🟡 Path Resolution Fragility

Location: packages/testtools/src/utils.ts:4 and tests/e2e/orm/scripts/generate.ts:26

Issues:

  1. Testtools uses require.resolve() - This works but may fail in pure ESM environments
  2. E2E tests use hardcoded relative paths - path.resolve(_dirname, '../../node_modules/@zenstackhq/plugin-policy/plugin.zmodel') is fragile across different execution contexts

Recommendation: Use import.meta.resolve() consistently with proper error handling.

🟠 Incomplete Migration

Location: packages/language/src/validators/function-invocation-validator.ts:229-232

// TODO: move this to policy plugin
@func('check')
private _checkCheck(expr: InvocationExpr, accept: ValidationAcceptor) {

Issue: The check function validation remains in the language package instead of being moved to the policy plugin, creating inconsistency.

🐛 Potential Bugs

  1. Missing error handling in generate.ts:81-83:

    try {
        cliPlugin = (await import(moduleSpec)).default as CliPlugin;
    } catch {
        // plugin may not export a generator so we simply ignore the error here
    }

    Silent failures make debugging plugin issues difficult.

  2. Documentation inconsistency in plugin.zmodel:64-65:

    "all" operation is expanded to "create", "read", "update", and "delete" rules
    

    This excludes "post-update" which is mentioned elsewhere in the file.

  3. Provider path inconsistency in samples/blog/zenstack/schema.zmodel:13:

    plugin policy {
        provider = '../node_modules/@zenstackhq/plugin-policy/dist/index.js'
    }

    Hardcoded relative paths are fragile. Should use package name: '@zenstackhq/plugin-policy'

🔒 Security Assessment

  • High Risk: Plugin discovery mechanism needs input validation to prevent path injection
  • Medium Risk: Dynamic import resolution could be exploited without proper sanitization
  • Low Risk: Policy handling maintains appropriate access controls

Performance Considerations

  1. Plugin discovery overhead - getPluginDocuments() performs multiple filesystem operations and could benefit from caching
  2. Multiple resolution attempts - The fallback chain (file path → ESM resolution) adds latency but provides robustness

🧪 Test Coverage

  • Good: Policy plugin tests appear comprehensive
  • Missing: No integration tests for the new plugin discovery mechanism
  • Fragile: E2E test path resolution may break in different environments

📝 Recommendations

Immediate (Security)

  1. Sanitize plugin provider paths in getPluginDocuments() to prevent path injection
  2. Implement plugin source whitelist for production environments

Short-term (Consistency)

  1. Complete the migration of check function validation to policy plugin
  2. Standardize path resolution using import.meta.resolve() across all contexts
  3. Add integration tests for plugin discovery mechanism

Medium-term (Performance)

  1. Implement caching for plugin discovery results
  2. Add plugin discovery performance metrics

🎯 Verdict

This refactoring represents solid architectural progress with good separation of concerns. The main security concern around plugin path injection should be addressed before merging. The other issues are mostly about consistency and robustness.

Overall Assessment: Approve with required security fixes


* fix: clean up plugin loading and fix VSCode extension

* address pr comments
@claude
Copy link

claude bot commented Oct 17, 2025

Claude finished @ymc9's task —— View job


PR Review for v3.0.0-beta.10 Policy Plugin Refactoring

I'll provide a comprehensive review covering code quality, potential bugs, performance, security, and test coverage.

Review Checklist

  • Analyze overall architectural changes and scope
  • Review plugin discovery mechanism for security/reliability
  • Check path resolution approaches and potential issues
  • Examine version consistency across packages
  • Assess security implications of dynamic plugin loading
  • Review test coverage and integration points
  • Evaluate performance considerations
  • Check for potential bugs and edge cases

🎯 Summary

This PR represents a significant architectural improvement by extracting policy attributes (@@allow, @@deny) from the core stdlib.zmodel to a dedicated plugin.zmodel file in the policy plugin package. The refactoring enhances modularity and separation of concerns while maintaining backward compatibility.

Positive Changes

  1. Clean architectural separation - Moving policy declarations from packages/language/res/stdlib.zmodel to packages/plugins/policy/plugin.zmodel improves modularity
  2. Robust plugin discovery - The new getPluginDocuments() function provides multiple fallback mechanisms for locating plugin model files
  3. Consistent version management - All 18 packages correctly bumped to 3.0.0-beta.10
  4. Backward compatibility maintained - Existing APIs and user schemas continue to work

⚠️ Critical Issues

🔴 Security Vulnerability - Plugin Path Injection

Location: packages/language/src/utils.ts:614

const resolvedUrl = import.meta.resolve(`${provider}/${PLUGIN_MODULE_NAME}`);

Risk: The provider value from user schemas is directly interpolated into import.meta.resolve() without sanitization. Malicious schemas could inject arbitrary module paths:

plugin malicious {
    provider = "../../../etc/passwd"  // Path traversal attempt
}

Recommendation: Sanitize provider paths and implement a whitelist of allowed plugin sources.

🟡 Path Resolution Fragility

Location: packages/testtools/src/utils.ts:4 and tests/e2e/orm/scripts/generate.ts:26

Issues:

  1. Testtools uses require.resolve() - This works but may fail in pure ESM environments
  2. E2E tests use hardcoded relative paths - path.resolve(_dirname, '../../node_modules/@zenstackhq/plugin-policy/plugin.zmodel') is fragile across different execution contexts

Recommendation: Use import.meta.resolve() consistently with proper error handling.

🟠 Incomplete Migration

Location: packages/language/src/validators/function-invocation-validator.ts:362

// TODO: move this to policy plugin
export function isCheckInvocation(node: AstNode) {

Issue: The check function validation remains in the language package instead of being moved to the policy plugin, creating inconsistency.

🐛 Potential Bugs

  1. Missing error handling in generate.ts:81-83:

    try {
        cliPlugin = (await import(moduleSpec)).default as CliPlugin;
    } catch {
        // plugin may not export a generator so we simply ignore the error here
    }

    Silent failures make debugging plugin issues difficult.

  2. Documentation inconsistency in plugin.zmodel:64-65:

    "all" operation is expanded to "create", "read", "update", and "delete" rules
    

    This excludes "post-update" which is mentioned elsewhere in the file.

  3. Provider path inconsistency in samples/blog/zenstack/schema.zmodel:14:

    plugin policy {
        provider = '../node_modules/@zenstackhq/plugin-policy/dist/index.js'
    }

    Hardcoded relative paths are fragile. Should use package name: '@zenstackhq/plugin-policy'

🔒 Security Assessment

  • High Risk: Plugin discovery mechanism needs input validation to prevent path injection
  • Medium Risk: Dynamic import resolution could be exploited without proper sanitization
  • Low Risk: Policy handling maintains appropriate access controls

Performance Considerations

  1. Plugin discovery overhead - getPluginDocuments() performs multiple filesystem operations and could benefit from caching
  2. Multiple resolution attempts - The fallback chain (file path → ESM resolution) adds latency but provides robustness

🧪 Test Coverage

  • Good: Policy plugin tests appear comprehensive
  • Missing: No integration tests for the new plugin discovery mechanism
  • Fragile: E2E test path resolution may break in different environments

📝 Recommendations

Immediate (Security)

  1. Sanitize plugin provider paths in getPluginDocuments() to prevent path injection
  2. Implement plugin source whitelist for production environments

Short-term (Consistency)

  1. Complete the migration of check function validation to policy plugin
  2. Standardize path resolution using import.meta.resolve() across all contexts
  3. Add integration tests for plugin discovery mechanism

Medium-term (Performance)

  1. Implement caching for plugin discovery results
  2. Add plugin discovery performance metrics

🎯 Verdict

This refactoring represents solid architectural progress with good separation of concerns. The main security concern around plugin path injection should be addressed before merging. The other issues are mostly about consistency and robustness.

Overall Assessment: Approve with required security fixes


Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/cli/src/actions/migrate.ts (1)

85-90: LGTM! Command construction improved.

The refactoring to a multi-line array format improves readability and adding --skip-generate brings consistency with runDev. The conditional --force logic is correct.

Minor style note: Consider adding a trailing comma after line 89 for consistency with runDev (line 74) and better diffs.

        const cmd = [
            'prisma migrate reset',
            ` --schema "${prismaSchemaFile}"`,
            ' --skip-generate',
-            options.force ? ' --force' : ''
+            options.force ? ' --force' : '',
        ].join('');
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c8f756 and 6f48c74.

📒 Files selected for processing (1)
  • packages/cli/src/actions/migrate.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/cli/src/actions/migrate.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: claude-review
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: build-test (20.x, postgresql)

@ymc9 ymc9 added this pull request to the merge queue Oct 17, 2025
Merged via the queue into main with commit 807891e Oct 17, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants